package com.github.natholdallas.common

import com.github.natholdallas.declaration.Getter
import com.github.natholdallas.declaration.Setter
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.repository.findByIdOrNull

@Suppress("UNUSED")
fun <T, ID : Any> JpaRepository<T, ID>.byId(id: ID): T {
    return getReferenceById(id)
}

@Suppress("UNUSED")
fun <T, ID : Any, R> JpaRepository<T, ID>.byId(id: ID, tf: (T) -> R): R {
    return tf(getReferenceById(id))
}

@Suppress("UNUSED")
fun <T, ID : Any, R> JpaRepository<T, ID>.byId(id: ID, tf: (T) -> R, mw: (T) -> Unit): R {
    val v = getReferenceById(id)
    mw(v)
    return tf(v)
}

@Suppress("UNUSED")
fun <T, ID : Any> JpaRepository<T, ID>.byIdOrNull(id: ID?): T? {
    if (id === null) return null
    return findByIdOrNull(id)
}

@Suppress("UNUSED")
fun <T, ID : Any, R> JpaRepository<T, ID>.byIdOrNull(id: ID?, tf: (T) -> R): R? {
    if (id === null) return null
    val v = findByIdOrNull(id) ?: return null
    return tf(v)
}

@Suppress("UNUSED")
fun <T, ID : Any, R> JpaRepository<T, ID>.byIdOrNull(id: ID?, tf: (T) -> R, mw: (T) -> Unit): R? {
    if (id === null) return null
    val v = findByIdOrNull(id) ?: return null
    mw(v)
    return tf(v)
}

@Suppress("UNUSED")
fun <T : Any, ID> JpaRepository<T, ID>.modifyThenSave(v: T, s: Setter<T>): T {
    s.set(v)
    return save(v)
}

@Suppress("UNUSED")
fun <T : Any, ID> JpaRepository<T, ID>.modifyThenSave(v: T, s: Setter<T>, mw: (T) -> Unit): T {
    mw(v)
    s.set(v)
    return save(v)
}

@Suppress("UNUSED")
fun <T : Any, ID> JpaRepository<T, ID>.modifyThenSave(v: T, s: (T) -> Unit): T {
    s(v)
    return save(v)
}

@Suppress("UNUSED")
fun <T, ID : Any> JpaRepository<T, ID>.byIdThenUpdate(id: ID, s: Setter<T>): T {
    val v = getReferenceById(id)
    s.set(v)
    return save(v)
}

@Suppress("UNUSED")
fun <T, ID : Any> JpaRepository<T, ID>.byIdThenUpdate(id: ID, s: Setter<T>, mw: (T) -> Unit): T {
    val v = getReferenceById(id)
    mw(v)
    s.set(v)
    return save(v)
}

@Suppress("UNUSED")
fun <T, ID : Any> JpaRepository<T, ID>.byIdThenUpdate(id: ID, s: (T) -> Unit): T {
    val v = getReferenceById(id)
    s(v)
    return save(v)
}

@Suppress("UNUSED")
fun <T, ID : Any> JpaRepository<T, ID>.byIdThenDelete(id: ID, mw: (T) -> Unit) {
    val v = getReferenceById(id)
    mw(v)
    delete(v)
}

@Suppress("UNUSED")
fun <T, ID> JpaRepository<T, ID>.all(): List<T> {
    return findAll()
}

@Suppress("UNUSED")
fun <T, ID, R> JpaRepository<T, ID>.all(tf: (T) -> R): List<R> {
    return findAll().map(tf)
}

@Suppress("UNUSED")
fun <T, ID> JpaRepository<T, ID>.all(page: Pageable): Page<T> {
    return findAll(page)
}

@Suppress("UNUSED")
fun <T, ID, R> JpaRepository<T, ID>.all(page: Pageable, tf: (T) -> R): Page<R> {
    return findAll(page).map(tf)
}

@Suppress("UNUSED")
fun <T, ID> JpaRepository<T, ID>.save(vararg entities: T): List<T> {
    return saveAll(entities.toList())
}

@Suppress("UNUSED")
fun <T, ID> JpaRepository<T, ID>.save(entities: Iterable<T>): List<T> {
    return saveAll(entities)
}

@Suppress("UNUSED")
fun <T : Any, ID> JpaRepository<T, ID>.save(g: Getter<T>): T {
    return save(g.get())
}

@Suppress("UNUSED")
fun <T : Any, ID> JpaRepository<T, ID>.save(g: Getter<T>, mw: (T) -> Unit): T {
    val model = g.get()
    mw(model)
    return save(model)
}
